﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

namespace SynoManager.GUI
{
    public partial class MainForm : Form, IHost
    {
        private SynoDevice device;
        internal PluginManager plugins;
        private bool advancedLayout = false;

        private bool isValidConnection = false;
        private bool isConnected = false;

        public MainForm()
        {
            InitializeComponent();
            notifyIcon.Icon = this.Icon;

            TestAndUpgradeConfiguration();

#if !DEBUG
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
            Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
#endif
        }

        private void TestAndUpgradeConfiguration()
        {
            System.Reflection.Assembly a = System.Reflection.Assembly.GetExecutingAssembly();
            Version appVersion = a.GetName().Version;
            string appVersionString = appVersion.ToString();

            if (Properties.Settings.Default.ConfigVersion != appVersion.ToString())
            {
                Properties.Settings.Default.Upgrade();
                Properties.Settings.Default.ConfigVersion = appVersionString;
            }
        }

        #region Exception Handler
        void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
        {
            Exception ex = e.Exception;
            ExceptionForm form = new ExceptionForm(ex);
            form.ShowDialog();
            Environment.Exit(1);
        }

        void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            Exception ex = e.ExceptionObject as Exception;
            ExceptionForm form = new ExceptionForm(ex);
            form.ShowDialog();
            Environment.Exit(1);
        }
        #endregion

        private void MainForm_Load(object sender, EventArgs e)
        {
            UpdateChecker.AutoCheckUpdateAndShowWindow(this);

            Size = Properties.Settings.Default.MainWindowSize;

            LoadConnection();
            OpenConnection();
            LoadInterface();
            
        }

        #region Connection
        private void LoadConnection()
        {
            if (Properties.Settings.Default.AutoConnect)
            {
                try
                {
                    LoadConnection(Properties.Settings.Default.Address,
                        Properties.Settings.Default.Username, ConnectionDialog.GetStoredPassword(), Properties.Settings.Default.Port, Properties.Settings.Default.SecurePort, Properties.Settings.Default.PreferSecure);
                }
                catch (Exception)
                {
                    LoadConnectionFromDialog();
                }
            }
            else
            {
                LoadConnectionFromDialog();
            }
        }

        private void LoadConnectionFromDialog()
        {
            ConnectionDialog dialog = new ConnectionDialog();
            if (dialog.ShowDialog(this) == DialogResult.OK)
            {
                LoadConnection(dialog);
            }
            else
            {
                // ConnectionDialog was cancelled
                Environment.Exit(0);
                return;
            }
        }

        private void LoadConnection(ConnectionDialog dialog)
        {
            LoadConnection(dialog.Address, dialog.Username, dialog.Password, dialog.Port, dialog.SecurePort, dialog.PreferSecure);
        }

        private void LoadConnection(string address, string username, string password, int port, int securePort, bool preferSecure)
        {
            device = new SynoDevice(address, username, password, port, securePort, preferSecure);
        }

        private void OpenConnection()
        {
            Debug.Assert(device != null, "SynoDevice not yet initialized");

            isValidConnection = false;
            IsConnected = false;

            try
            {
                device.Open();
                device.ConnectionStateChanged += new EventHandler(deviceConnectionStateChanged);
                IsConnected = true;
            }
            catch (AuthenticationException ex)
            {
                MessageBox.Show(this, ex.Message, "Could not connect", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Reconnect(null, null);
            }
            catch (CommunicationException ex)
            {
                MessageBox.Show(this, ex.Message, "Could not connect", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Reconnect(null, null);
            }
            ProcessQueuedCommendLineArguments();
        }

        private void Reconnect(object sender, EventArgs e)
        {
            Debug.WriteLine("Reconnecting", "MainForm");
            ConnectionDialog dialog = new ConnectionDialog();
            if (dialog.ShowDialog(this) == DialogResult.OK)
            {
                if (device != null && device.IsOpen)
                    device.Close();

                LoadConnection(dialog);
                OpenConnection();
                LoadInterface();
            }
        }

        #endregion

        private void LoadPlugins()
        {
            if (plugins == null)
            {
                plugins = new PluginManager();
                plugins.SetHost(this);
            }
        }

        private void LoadInterface()
        {
            tabControl1.TabPages.Clear();
            splitContainer1.Panel1Collapsed = !advancedLayout;

            LoadPlugins();

            Debug.Assert(plugins.Plugins.Count() > 0, "No plugins found");

            plugins.Initialize(device);
            Debug.Assert(!InvokeRequired);
            foreach(var plugin in plugins.Plugins)
            {
                string key = plugin.GetType().Name;

                // Main Tab
                Control control = plugin.MainControl;
                if (control != null)
                {
                    tabControl1.TabPages.Add(key, control.Text);
                    tabControl1.TabPages[key].Controls.Add(control);
                    tabControl1.TabPages[key].Tag = plugin;
                    plugin.TabActive = (tabControl1.TabCount == 1);
                    control.Dock = DockStyle.Fill;
                }
            }
        }

        #region IHost Members

        MenuStrip IHost.Menu
        {
            get { return menuStrip; }
        }

        ToolStripContainer IHost.ToolStrips
        {
            get { return toolStripContainer; }
        }

        StatusStrip IHost.StatusStrip
        {
            get { return statusStrip; }
        }

        NotifyIconEx IHost.NotifyIcon
        {
            get { return notifyIcon; }
        }
#endregion

        #region Connection State
        /// <summary>
        /// Gets wether the connection settings were valid. 
        /// </summary>
        /// <remarks>This will return true if the connection was setup once successfully</remarks>
        public bool IsValidConnection
        {
            get { return isValidConnection; }
            internal set { isValidConnection = value; }
        }

        /// <summary>
        /// Event that is throws when the IsConnected property has changed
        /// </summary>
        public event EventHandler ConnectionStateChanged;

        /// <summary>
        /// Gets wether the main interface is connected to a device.
        /// </summary>
        public bool IsConnected
        {
            get { return isConnected; }
            set 
            {
                // save old value for ConnectionStateChange event
                bool oldValue = isConnected;

                // update value
                isConnected = value;

                // if connected, also set isValidConnection
                if (isConnected)
                    isValidConnection = true;

                // throw event if state changed
                if (oldValue != value)
                {
                    updateConnectionStateStatusUI();

                    if (ConnectionStateChanged != null)
                        ConnectionStateChanged(this, EventArgs.Empty);
                }
            }
        }

        void updateConnectionStateStatusUI()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(updateConnectionStateStatusUI));
                return;
            }

            // update status strip:
            connectionStateToolStripStatusLabel.Image = (isConnected)
                                                            ? Properties.Resources.connect
                                                            : Properties.Resources.disconnect;
            if (isConnected)
            {
                connectionStateToolStripStatusLabel.Text = device.Address;
            }
            else
            {
                connectionStateToolStripStatusLabel.Text = (isValidConnection) ? "Connection Lost" : "Not Connected";
            }

            if (plugins != null)
                plugins.ConnectionStateChange(isConnected, isValidConnection);
        }

        void deviceConnectionStateChanged(object sender, EventArgs e)
        {
            IsConnected = device.IsConnected;
        }

        #endregion

        private void tabControl1_Selected(object sender, TabControlEventArgs e)
        {
            if (e.TabPage != null)
            {
                var plugin = e.TabPage.Tag as IGuiPlugin;
                if (plugin != null)
                    plugin.TabActive = true;
            }
        }

        private void tabControl1_Deselected(object sender, TabControlEventArgs e)
        {
            if (e.TabPage != null)
            {
                var plugin = e.TabPage.Tag as IGuiPlugin;
                if (plugin != null)
                    plugin.TabActive = false;
            }
        }

        private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (device != null && device.IsOpen)
                device.Close();
            notifyIcon.Visible = false;
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing)
            {
                ShowInTaskbar = false;
                this.Hide();
                e.Cancel = true;
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show(this, "Are you sure you wish to exit SynoManager?", "Exit SynoManager?", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == System.Windows.Forms.DialogResult.Yes)
            {
                if (device != null && device.IsOpen)
                    device.Close();
                notifyIcon.Visible = false;
                notifyIcon.Dispose();
                Properties.Settings.Default.MainWindowSize = this.Size;
                Properties.Settings.Default.Save();
                Environment.Exit(0);
            }
        }

        private void minimizeToTrayToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowInTaskbar = false;
            this.Hide();
        }

        private void notifyIcon_DoubleClick(object sender, EventArgs e)
        {
            if (!ShowInTaskbar)
            {
                this.ShowInTaskbar = true;
                this.Show();
            }

            // Make sure we are the active window
            Activate();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutForm form = new AboutForm();
            form.ShowDialog(this);
        }

        private string queuedCommandLineArguments = null;

        internal void Startup(ReadOnlyCollection<string> eventArgs)
        {
            Debug.Assert(queuedCommandLineArguments == null);
            queuedCommandLineArguments = String.Join(" ", eventArgs.ToArray());
            if (queuedCommandLineArguments.Length == 0)
                queuedCommandLineArguments = null;
            else
                ProcessQueuedCommendLineArguments();
        }

        void ProcessQueuedCommendLineArguments()
        {
            if (String.IsNullOrEmpty(queuedCommandLineArguments))
                return;

            if (device == null)
                return; // queued, so it will be called again;

            //TODO: Refactor to general Command Line Arguments parser and processor
            try
            {
                device.FindService<DownloadStation.DownloadStationService>().Add(queuedCommandLineArguments);
            }
            catch (SynoManager.DownloadStation.InvalidDownloadLocationException ex)
            {
                MessageBox.Show(this, ex.Message, "Invalid Location", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            finally
            {
                queuedCommandLineArguments = null;
            }
        }

        private void preferencesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PreferencesForm form = new PreferencesForm(this);
            form.ShowDialog();
        }
    }
}
